package fr.ul.ade.prime.ui;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.database.ContentObserver;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.provider.BaseColumns;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.support.v4.widget.CursorAdapter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemLongClickListener;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;

import com.actionbarsherlock.app.SherlockListFragment;

import fr.ul.ade.R;
import fr.ul.ade.prime.model.AdeWebOptions;
import fr.ul.ade.prime.model.ScheduleState;
import fr.ul.ade.prime.provider.AdeWebContract;
import fr.ul.ade.prime.ui.ScheduleList.ScheduleActionMode;

public class ScheduleListFragment extends SherlockListFragment implements
		LoaderManager.LoaderCallbacks<Cursor>, OnItemLongClickListener,
		BundleExtraNames, OnClickListener {

	protected static final String TAG = ScheduleListFragment.class
			.getSimpleName();

	protected static final int[] ICON_ID = { R.drawable.status_other,
			R.drawable.status_waiting_for_update, R.drawable.status_updating,
			R.drawable.status_updating, R.drawable.status_updating,
			R.drawable.status_updating, R.drawable.status_updating,
			R.drawable.status_updating, R.drawable.status_not_updated,
			R.drawable.status_updated };

	private ScheduleListAdapter mAdapter;

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		this.mAdapter = new ScheduleListAdapter(this.getActivity());
		this.setListAdapter(this.mAdapter);
	}

	@Override
	public void onStart() {
		super.onStart();
		// On récupère le loader
		Loader<Cursor> loader = getLoaderManager()
				.getLoader(ScheduleQuery._TOKEN);
		if (loader != null) {
			// On force le rechargment des données.
			loader.forceLoad();
		}
	}

	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container,
			Bundle savedInstanceState) {
		return inflater.inflate(R.layout.lesson_list, container, false);
	}

	@Override
	public void onViewCreated(View view, Bundle savedInstanceState) {
		super.onViewCreated(view, savedInstanceState);
		final ListView listView = this.getListView();
		listView.setSelector(android.R.color.transparent);
		listView.setEmptyView(view.findViewById(R.id.empty));
		listView.setOnItemLongClickListener(this);
	}

	@Override
	public void onActivityCreated(Bundle savedInstanceState) {
		super.onActivityCreated(savedInstanceState);

		// As of support library r8, calling initLoader for a fragment in a
		// FragmentPagerAdapter in the fragment's onCreate may cause the same
		// LoaderManager to be
		// dealt to multiple fragments because their mIndex is -1 (haven't been
		// added to the
		// activity yet). Thus, we do this in onActivityCreated.

		// On initialise le loader qui s'occupera de charger la liste des edts.
		// Champs :
		// * id du loader (ici il n'y en n'a qu'on donc il n'est pas important)
		// * arguments sous forme d'un Bundle
		// * l'objet qui sera chargé de la création du loader et qui sera averti
		// des changements d'état du loader (chargement terminé ou reset)
		getLoaderManager().initLoader(ScheduleQuery._TOKEN, null, this);
	}

	// Cet objet est un écouteur pour les changements fait sur les données du
	// provider.
	// Il est appellé suite à un changement des données.
	// Dans ce cas, on se contente de demander au loader de recharger la liste
	// entière des edts.
	private final ContentObserver mObserver = new ContentObserver(new Handler()) {
		@Override
		public void onChange(boolean selfChange) {
			// Si jamais des données sont modifiées avant que le fragment soit
			// ajouté à l'activité
			if (getActivity() == null) {
				return;
			}
			// Log.d(TAG, "Schedule changed, added or deleted !");
			// On récupère le loader
			Loader<Cursor> loader = getLoaderManager()
					.getLoader(ScheduleQuery._TOKEN);
			if (loader != null) {
				// On force le rechargment des données.
				loader.forceLoad();
			}
		}
	};

	// On ajoute l'écouteur pour les changements des données en précisant :
	// - l'URI des données à observer (ici tout ce qui concerne les edts)
	// - flag indiquant si on veut être alerte des changements des URI fils
	// (plus précises que celle fournie avant,
	// sinon on ne sera alerté que pour les URIs qui correspondent exactement)
	// Example : URI -> /schedule
	// Alors un changement concernant l'URI /schedule/1 (a = identifiant d'un
	// edt) ne provoque
	// on appel de l'écouteur que si le flag est à true
	// - objet ContentObserver = écouteur qui sera appelé
	@Override
	public void onAttach(Activity activity) {
		super.onAttach(activity);
		activity.getContentResolver()
				.registerContentObserver(AdeWebContract.Schedule.CONTENT_URI,
						true,
						this.mObserver);
	}

	// On supprime l'écouteur
	@Override
	public void onDetach() {
		super.onDetach();
		getActivity().getContentResolver()
				.unregisterContentObserver(this.mObserver);
	}

	// On crée le loader, cette fonction est appelée automatiquement par le
	// LoaderManager
	// Dans notre cas, on se contente d'instancier CursorLoader qui est une
	// sous-classe
	// de Loader<Cursor> (charge de manière asynchrone (-> AsyncTask) les
	// données précisés)
	@Override
	public Loader<Cursor> onCreateLoader(int id, Bundle data) {
		Uri scheduleUri = AdeWebContract.Schedule.CONTENT_URI;
		String[] projection = ScheduleQuery.PROJECTION;
		String selection = null;
		String[] selectionArgs = null;
		return new CursorLoader(this.getActivity(), scheduleUri, projection,
				selection, selectionArgs, AdeWebContract.Schedule.DEFAULT_SORT);
	}

	// Cette fonction est appelé à chaque fois qu'un loader termine son
	// chargement. Dans notre cas, on se content de passer le nouveau
	// Cursor (qui contient les données) à l'adaptateur qui fera les
	// changements nécessaires dans l'acitivité.
	@Override
	public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
		if (this.getActivity() == null) {
			return;
		}// if
		this.mAdapter.changeCursor(cursor);

		final View view = this.getView();
		view.findViewById(R.id.list_loading).setVisibility(View.GONE);

		final ScheduleList activity = (ScheduleList) this.getActivity();
		this.getView()
				.setBackgroundColor(AdeWebOptions.getBackgroundColor(AdeWebOptions
						.getTheme(this.getActivity())));
		if (cursor.getCount() == 0) {
			final LayoutInflater inflater = activity.getLayoutInflater();
			final ListView listView = this.getListView();
			if (listView.getEmptyView() == null) {
				final View emptyView = inflater
						.inflate(R.layout.lesson_list_empty,
								(ViewGroup) listView.getParent(),
								true).findViewById(android.R.id.empty);
				listView.setEmptyView(emptyView);

				final TextView createText = (TextView) emptyView
						.findViewById(R.id.update_schedule);
				createText.setText(R.string.ScheduleList_no_schedule);
				createText
						.setTextColor(AdeWebOptions.getTextColor(AdeWebOptions
								.getTheme(this.getActivity())));
				createText.setOnClickListener(this);
			}// if
		} else {
			activity.updateProgressBar(this.mAdapter);
		}// if
	}// onLoadFinished(Loader<Cursor>, Cursor)

	// Cette fonction est appelé à chaque fois que les données d'un
	// loader deviennent inaccessibles. Si on a des références vers
	// ces données, il faut les supprimer.
	@Override
	public void onLoaderReset(Loader<Cursor> loader) {
		/* Nothing to be done. */
	}

	@Override
	public void onListItemClick(ListView l, View v, int position, long id) {
		Intent intent = new Intent(this.getActivity(), ViewSchedule.class);
		Cursor cursor = (Cursor) this.mAdapter.getItem(position);
		intent.putExtra(EXTRA_SCHEDULE_ID, cursor.getLong(ScheduleQuery._ID));
		intent.putExtra(EXTRA_SCHEDULE_NAME,
				cursor.getString(ScheduleQuery.SCHEDULE_NAME));
		intent.putExtra(EXTRA_SCHEDULE_PERIOD,
				cursor.getInt(ScheduleQuery.SCHEDULE_PERIOD));
		this.getActivity().startActivity(intent);
	}

	public void updateSchedule() {
		ScheduleList activity = (ScheduleList) this.getActivity();
		int nSchedules = this.mAdapter.getCount();
		for (int s = 0; s < nSchedules; ++s) {
			Cursor cursor = (Cursor) this.mAdapter.getItem(s);
			long scheduleId = cursor.getLong(ScheduleQuery._ID);
			activity.service.launchScheduleUpdate(scheduleId);
		}// for
	}// updateSchedule()

	// Un adaptateur classique qui hérite de CursorAdapter (adaptateur qui
	// stocke ces données
	// sous forme d'un cursor).
	private class ScheduleListAdapter extends CursorAdapter {

		private Activity mActivity;

		public ScheduleListAdapter(Activity activity) {
			super(activity, null, false);
			this.mActivity = activity;
		}

		@Override
		public void bindView(View view, Context context, Cursor cursor) {
			final TextView scheduleName = (TextView) view
					.findViewById(R.id.schedule_name);
			scheduleName.setText(cursor.getString(ScheduleQuery.SCHEDULE_NAME));
			// TODO a enlever si recréation de l'activité au changement de thème
			scheduleName.setTextColor(AdeWebOptions.getTextColor(AdeWebOptions
					.getTheme(ScheduleListFragment.this.getActivity())));

			final ImageView icon = (ImageView) view
					.findViewById(R.id.schedule_status);
			icon.setImageResource(ICON_ID[cursor
					.getInt(ScheduleQuery.SCHEDULE_STATE)]);
		}

		@Override
		public View newView(Context context, Cursor cursor, ViewGroup parent) {
			return this.mActivity.getLayoutInflater()
					.inflate(fr.ul.ade.R.layout.schedule_listitem,
							parent,
							false);
		}
	}

	// Interface auxiliaire utilisé pour les requêtes vers le content provider
	// On retrouve ici les colonnes (données) nécessaires ainsi que des
	// variables
	// auxiliaires pour y accéder.
	public interface ScheduleQuery {
		// id pour le loader
		int _TOKEN = 0x1;

		// colonnes demandées au provider
		String[] PROJECTION = { BaseColumns._ID,
				AdeWebContract.Schedule.SCHEDULE_NAME,
				AdeWebContract.Schedule.SCHEDULE_STATUS,
				AdeWebContract.Schedule.SCHEDULE_PERIOD };

		// colonnes des données demandés pour utiliser lors de la récupération
		// des données depuis le Cursor (appels à getString(int) p.ex.)
		int _ID = 0;
		int SCHEDULE_NAME = 1;
		int SCHEDULE_STATE = 2;
		int SCHEDULE_PERIOD = 3;
	}

	@Override
	public boolean onItemLongClick(AdapterView<?> parent, View view,
			int position, long id) {
		ScheduleList activity = (ScheduleList) this.getActivity();
		Cursor cursor = (Cursor) this.mAdapter.getItem(position);
		activity.setDisplayedScheduleId(cursor.getLong(ScheduleQuery._ID));
		activity.setDisplayedScheduleName(cursor
				.getString(ScheduleQuery.SCHEDULE_NAME));
		activity.setDisplayedScheduleState(ScheduleState.values()[cursor
				.getInt(ScheduleQuery.SCHEDULE_STATE)]);
		activity.updateActionModeState();
		activity.mActionMode = activity.startActionMode(new ScheduleActionMode(
				activity));

		FragmentManager fm = this.getActivity().getSupportFragmentManager();
		Fragment detailsFragment = fm.findFragmentById(R.id.schedule_details);
		Fragment listFragment = fm.findFragmentById(R.id.schedule_list);
		FragmentTransaction ft = fm.beginTransaction();
		ft.setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out);
		ft.show(detailsFragment);
		ft.hide(listFragment);
		ft.commit();

		return true;
	}

	@Override
	public void onClick(View v) {
		Intent createIntent = new Intent(this.getActivity(),
				CreateSchedule.class);
		this.getActivity().startActivity(createIntent);
	}// onClick(View)

}// ScheduleList2